# -*- coding: utf-8 -*-
# @Time    : 2021/3/11 19:42
# @Author  : huangwei
# @File    : get_signature_method.py
# @Software: PyCharm
from PIL import Image
import numpy as np
import cv2

from paddleocr import PaddleOCR

ocr = PaddleOCR(use_angle_cls=False, lang='ch')


def exp( x ):
    # 将 数组中小于-6的值变为-6，大于6的值变为6
    x = np.clip(x, -6, 6)
    y = 1 / (1 + np.exp(-x))
    return y


def sort_box( box ):
    # 将四个点按左上、右上、右下、左下排序输出
    x1, y1, x2, y2, x3, y3, x4, y4 = box[:8]
    pts = (x1, y1), (x2, y2), (x3, y3), (x4, y4)
    pts = np.array(pts, dtype="float32")

    # 先按 x 坐标大小进行排序，即从左到右进行排序
    x_sorted = pts[np.argsort(pts[:, 0]), :]
    # 对最左边两个点进行上下排序，分出左上角和左下角
    left_points = x_sorted[:2, :]
    right_points = x_sorted[2:, :]

    left_sort = left_points[np.argsort(left_points[:, 1]), :]
    left_top, left_down = left_sort

    # 同理可分出右上和右下
    right_sort = right_points[np.argsort(right_points[:, 1]), :]
    right_top, right_down = right_sort

    (x1, y1), (x2, y2), (x3, y3), (x4, y4) = np.array([left_top, right_top, right_down, left_down], dtype="float32")

    return x1, y1, x2, y2, x3, y3, x4, y4


def get_line( coords ):
    """
    返回该点集的最小外接矩形的与较长边平行的中心线
    :param coords:
    :return:
    """
    rect = cv2.minAreaRect(coords[:, ::-1])
    box = cv2.boxPoints(rect)
    box = box.reshape((8,)).tolist()

    # 将外接矩形四个点进行排序确定每一个点的位置
    box = sort_box(box)
    x1, y1, x2, y2, x3, y3, x4, y4 = box

    # 计算出矩形的长宽
    w = (np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) + np.sqrt((x3 - x4) ** 2 + (y3 - y4) ** 2)) / 2
    h = (np.sqrt((x2 - x3) ** 2 + (y2 - y3) ** 2) + np.sqrt((x1 - x4) ** 2 + (y1 - y4) ** 2)) / 2

    if w < h:
        xmin = (x1 + x2) / 2
        xmax = (x3 + x4) / 2
        ymin = (y1 + y2) / 2
        ymax = (y3 + y4) / 2

    else:
        xmin = (x1 + x4) / 2
        xmax = (x2 + x3) / 2
        ymin = (y1 + y4) / 2
        ymax = (y2 + y3) / 2

    return [xmin, ymin, xmax, ymax]


def draw_lines( img, lines, color=(0, 0, 0), line_width=3 ):
    tmp = np.copy(img)

    for line in lines:
        x1, y1, x2, y2 = line
        cv2.line(tmp, (int(x1), int(y1)), (int(x2), int(y2)), color, line_width, lineType=cv2.LINE_AA)

    return tmp


def resize_img( img, size ):
    """ 将图片映射到SIZE大小的图片上并对周围进行填充，然后作为模型的输入 """
    # 修改 img 尺寸使其有一边与 size 相等
    w, h = size
    img_w, img_h = img.size
    ratio = min(w / img_w, h / img_h)

    new_w = int(img_w * ratio)
    new_h = int(img_h * ratio)

    new_img = img.resize((new_w, new_h), Image.BICUBIC)

    # 分别为长、宽变化的比例
    fx = new_w / img_w
    fy = new_h / img_h

    # 长宽剩下部分的一半的长度，用于将图片放置到中间四周进行填充
    dx = (w - new_w) // 2
    dy = (h - new_h) // 2

    # 对四周进行填充
    boxed_img = Image.new('RGB', size, (128, 128, 128))
    boxed_img.paste(new_img, (dx, dy))

    return boxed_img, fx, fy, dx, dy


def get_equation( p1, p2 ):
    """ 得到线段所在直线方程的一般式参数 """
    x1, y1 = p1
    x2, y2 = p2
    A = y2 - y1
    B = x1 - x2
    C = x2 * y1 - x1 * y2
    return A, B, C


def point_line( p, A, B, C ):
    """ 将点坐标带入直线方程的一般式中来判断点和线之间的关系 """
    x, y = p
    r = A * x + B * y + C
    return r


def dist( p1, p2 ):
    """ 两个点之间的距离 """
    return np.sqrt((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2)


def line_line( line1, line2, alpha=10 ):
    """ 计算两条线段之间的距离，如果存在两条线段延申的交点小于一个范围则连接 """
    x1, y1, x2, y2 = line1
    ox1, oy1, ox2, oy2 = line2
    A1, B1, C1 = get_equation((x1, y1), (x2, y2))
    A2, B2, C2 = get_equation((ox1, oy1), (ox2, oy2))
    flag1 = point_line([x1, y1], A2, B2, C2)
    flag2 = point_line([x2, y2], A2, B2, C2)

    # 如果两条线段不相交
    if (flag1 > 0 and flag2 > 0) or (flag1 < 0 and flag2 < 0):
        # 两条线段延长线的交点
        x = (B1 * C2 - B2 * C1) / (A1 * B2 - A2 * B1)
        y = (A2 * C1 - A1 * C2) / (A1 * B2 - A2 * B1)
        p = (x, y)
        r0 = dist(p, (x1, y1))
        r1 = dist(p, (x2, y2))

        if min(r0, r1) < alpha:
            if r0 < r1:
                line1 = [p[0], p[1], x2, y2]
            else:
                line1 = [x1, y1, p[0], p[1]]

    return line1


def minAreaRectBox( coords ):
    """ 连通区域的外接矩形 """
    rect = cv2.minAreaRect(coords[:, ::-1])
    box = cv2.boxPoints(rect)
    box = box.reshape((8,)).tolist()
    box = sort_box(box)
    return box


def img2trans( img_path, save_path ):
    """图片转为透明"""
    img = cv2.imread(img_path)
    shape = img.shape[0:2]

    # 先将其二值化用来找空白处坐标
    gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, bin_img = cv2.threshold(gray_img, thresh=190, maxval=255, type=cv2.THRESH_BINARY)  # 将大于80的像素点转为255

    # 将图片转为4通道，添加一个透明通道
    img_a = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)

    # 依次扫描每一个像素点，将其二值图中为255的位置的值改为 (0, 0, 0, 0)
    for h in range(shape[0]):
        for w in range(shape[1]):
            if bin_img[h][w] == 255:
                img_a[h][w] = [255, 255, 255, 0]

    # cv2.imwrite(save_path, img_a)
    # 保存文件名带中文名
    cv2.imencode('.png', img_a)[1].tofile(save_path)


def get_name( img, boxes, index ):
    """ 从传入的四个boxes 中提取中签名照片来 """
    # 对 boxes 进行从左到右的排序
    center_x = []
    for box in boxes:
        x1, y1, x2, y2, x3, y3, x4, y4 = box
        cx = (x1 + x2 + x3 + x4) / 4
        center_x.append(cx)

    center_x = np.array(center_x)
    sort_index = np.argsort(center_x)

    boxes = np.array(boxes)
    boxes = boxes[sort_index]
    boxes = boxes.tolist()

    image = np.array(img)
    # 截取出第一个box来，需要高大于宽，否则为第一行不截取
    # 假定这个box是平行的无需旋转
    sorted_box = sort_box(boxes[0])
    x1, y1, x2, y2, x3, y3, x4, y4 = sorted_box

    if (y3 - y1) > (x3 - x1):
        # 识别第二个box中的文字作为文件名
        sorted_box = sort_box(boxes[1])
        x1, y1, x2, y2, x3, y3, x4, y4 = sorted_box

        crop_img = image[int(y1):int(y3), int(x1):int(x3)]
        crop_img_path = "tmp_path/crop_name%d.png" % index
        cv2.imwrite(crop_img_path, crop_img)

        ocr_result = ocr.ocr(crop_img_path)
        # 提取出识别出的名字来，取识别出的第一个str为文件名，如果没有识别出str则随机分配一个名字
        txts = [line[1][0] for line in ocr_result]

        if len(txts) == 0:
            filename = "output/%d_signature.png" % index
        else:
            filename = "output/%s_signature.png" % (txts[0])

        # 截取第四个框，并保存文件
        sorted_box = sort_box(boxes[3])
        x1, y1, x2, y2, x3, y3, x4, y4 = sorted_box

        crop_img = image[int(y1):int(y3), int(x1):int(x3)]
        temp_crop_path = "tmp_path/crop_signature%d.png" % index
        cv2.imwrite(temp_crop_path, crop_img)

        # 将提取出的签名转换为透明的
        img2trans(temp_crop_path, filename)
